﻿/*  ----------------------------------------------------------------------------
 *  DES-Release 2
 *  ----------------------------------------------------------------------------
 *  File:       DESServerService.cs
 *  Author:     nitinkoshy@gmail.com
 *  Source:     http://des.codeplex.com/
 *  ----------------------------------------------------------------------------
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
using DES.Common;
using System.Configuration;
using DES.Server.Service.Config;
using System.Reflection;
using System.Diagnostics;
using DES.Server.Service.Workers;

namespace DES.Server.Service
{
    [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Multiple )]
    public class DESServerService : IDESServerService
    {
        private int _maxLoad  = 0;
        private List<DESServerInfo> _connectedServers = new List<DESServerInfo>();
        private DESServerInfo _serverInfo;
        private Queue<ServerTask> _tasksInQueue = new Queue<ServerTask>();
        private Queue<ServerTask> _processedTasksQueue = new Queue<ServerTask>();
        private List<ServerTask> _taskCollectionGate = new List<ServerTask>();
        private List<IServerWorker> _workers = new List<IServerWorker>();


        public DESServerService()
        {
            ReadConfigData();
            Trace.WriteLine(String.Format("Started Service - {0} at endpoint {1}.", _serverInfo.Id, _serverInfo.EndPoint));
            Trace.WriteLine(String.Format("Max Load : {0}", _maxLoad.ToString()));
            Trace.Write("ConnectedServers :");

            foreach(DESServerInfo serverInfo in _connectedServers)
            {
                Trace.Write(serverInfo.Id + " ");
            }

            Trace.WriteLine(String.Empty);
            InitWorkers();
            Trace.WriteLine("Done Initiating worker threads.");
            
        }

        private void InitWorkers()
        {
            _workers.Add(new LoadBalancer());
            _workers.Add(new Executor());
            _workers.Add(new Forwarder());
            _workers.Add(new CollectionGateKeeper());

            foreach (IServerWorker worker in _workers)
                worker.StartMonitoring(this);

        }

        private void ReadConfigData()
        {
            ConnectedServerConfigSection connectedServerSection = (ConnectedServerConfigSection) ConfigurationManager.GetSection("connectedServers");
            foreach (ServerElement serverElement in connectedServerSection.Servers)
                _connectedServers.Add(new DESServerInfo(serverElement.Id, serverElement.EndPoint));

            _maxLoad = Convert.ToInt16(ConfigurationManager.AppSettings["maxload"]);
            _serverInfo = new DESServerInfo(ConfigurationManager.AppSettings["id"], ConfigurationManager.AppSettings["endpoint"]);
        }

        public int MaxAllowedLoad
        {
            get
            {
                return _maxLoad;
            }
        }

        public int CurrentLoad
        {
            get
            {
                lock (_tasksInQueue)
                {
                    return _tasksInQueue.Count;
                }
            }
        }

        public DESServerInfo ServerInfo
        {
            get
            {
                return _serverInfo;
            }
        }

        public List<DESServerInfo> ConnectedServers
        {
            get
            {
                return _connectedServers;
            }
        }

        void IDESService.ProcessTask(ClientTask clientTask)
        {
            Trace.Write(String.Format("[{0}/{1}]:",ServerInfo.Id, DateTime.Now.ToShortTimeString()));
            Trace.WriteLine(String.Format("Request to process task {0} in type {1}", clientTask.Name, clientTask.TypeName));
            lock (_tasksInQueue)
            {
                _tasksInQueue.Enqueue(new ServerTask(clientTask));
            }
            Trace.Write(String.Format("[{0}/{1}]:", ServerInfo.Id, DateTime.Now.ToShortTimeString()));
            Trace.WriteLine(String.Format("Enqueued task {0} in type {1}", clientTask.Name, clientTask.TypeName));
        }

        int IDESServerService.GetServerLoad()
        {
            return CurrentLoad;
        }

        public Queue<ServerTask> TaskQueue
        {
            get
            {
                return _tasksInQueue;
            }
        }

        public Queue<ServerTask> ProcessedTasks
        {
            get
            {
                return _processedTasksQueue;
            }
        }

        public List<ServerTask> CollectionGate
        {
            get
            {
                return _taskCollectionGate;
            }
        }

        void IDESServerService.QueueTask(ServerTask serverTask, DESServerInfo originatingServer)
        {
            Trace.Write(String.Format("[{0}/{1}]:", ServerInfo.Id, DateTime.Now.ToShortTimeString()));
            Trace.WriteLine(String.Format("Request to queue task {0} from server {1}", serverTask.Task.Name, originatingServer.Id));
            serverTask.MarkAsForwarded(originatingServer);
            lock (TaskQueue)
            {
                TaskQueue.Enqueue(serverTask);
            }
            Trace.Write(String.Format("[{0}/{1}]:", ServerInfo.Id, DateTime.Now.ToShortTimeString()));
            Trace.WriteLine(String.Format("Task {0} from server {1} queued", serverTask.Task.Name, originatingServer.Id));
        }

        /// <summary>
        /// Called by an execution server onto parent server
        /// when the task has been completed
        /// </summary>
        /// <param name="serverTask"></param>
        void IDESServerService.SetProcessedTask(ServerTask serverTask, DESServerInfo originatingServer)
        {
            Trace.Write(String.Format("[{0}/{1}]:", ServerInfo.Id, DateTime.Now.ToShortTimeString()));
            Trace.WriteLine(String.Format("Processed Task {0} received from server {1}", serverTask.Task.Name, originatingServer.Id));
            serverTask.MarkAsReceived();

            if (CollectionGateKeeper.ShouldTaskWait(serverTask, this))
            {
                lock (CollectionGate)
                {
                    CollectionGate.Add(serverTask);
                }
                Trace.Write(String.Format("[{0}/{1}]:", ServerInfo.Id, DateTime.Now.ToShortTimeString()));
                Trace.WriteLine(String.Format("Server Message : task should wait : {0}", serverTask.Task.Name));
                CollectionGateKeeper.DumpCollectionGate(this);
            }
            else
            {
                lock (ProcessedTasks)
                {
                    ProcessedTasks.Enqueue(serverTask);
                }
                Trace.Write(String.Format("[{0}/{1}]:", ServerInfo.Id, DateTime.Now.ToShortTimeString()));
                Trace.WriteLine(String.Format("Processed Task {0} from server {1} enqueued to internal processed queue ", serverTask.Task.Name, originatingServer.Id));
            }
        }
    }
}

/*

IAutoPersist
{
  Hydrate()  
  ExpiryPeriod()
  SupportsNotification()
  Dehydrate()
}

IAutoPersistService
{
      
}
*/
